﻿using AutoMapper;
using FlyleafLib;
using FlyleafLib.MediaPlayer;
using MaterialDesignThemes.Wpf;
using Morin.Services;
using Morin.Shared.Configs;
using Morin.Shared.Models;
using Morin.Wpf.Messages.Players;
using Stylet;
using StyletIoC;
using System.Collections.Generic;
using System.Windows;
using IContainer = StyletIoC.IContainer;

namespace Morin.Wpf.ViewModels.Players;

public class PlayerViewModel(
    IEventAggregator eventAggregator,
    AppSettingsConfig appSettingsConfig,
    IAppService appService,
    IContainer container,
    IMapper mapper,
    ISnackbarMessageQueue snackbarMessageQueue) :
    Conductor<Screen>,
    IHandle<OnEpisodeChangedMessage>
{
    public WindowState WindowState { get; set; }
    public bool IsPlay { get; set; }
    public bool IsFavorite { get; set; }
    private VideoSettingsConfig? _videoSettings;
    private List<PlaySkipTimeModel>? _playSkipTimes;
    public Dictionary<string, IEnumerable<VideoModel>>? PlayDict { get; set; }
    public int PlayerListWidth { get; set; } = 300;
    public bool ToClose { get; set; }

    public string Title
    {
        get
        {
            const string baseTitle = "Morin";
            return $"{baseTitle} {_curVideo?.VodName}";
        }
    }

    public Player? Player { get; set; }
    public Config? Config { get; set; }
    public ISnackbarMessageQueue MessageQueue { get; } = snackbarMessageQueue;
    public PlayMode PlayMode { get; set; } = PlayMode.Video;
    private int _skipBeginPosition;

    public int SkipBeginPosition
    {
        get => _skipBeginPosition;
        set
        {
            _skipBeginPosition = value;
            SkipTimeUpdate();
            SkipBegin();
        }
    }

    private int _skipEndPosition;

    public int SkipEndPosition
    {
        get => _skipEndPosition;
        set
        {
            _skipEndPosition = value;
            SkipTimeUpdate();

            if (IsSkipEnd()) NavigateNext();
        }
    }

    private bool _skipTimeSwitch;

    public bool SkipTimeSwitch
    {
        get => _skipTimeSwitch;
        set
        {
            _skipTimeSwitch = value;
            SkipTimeUpdate();
        }
    }

    private VideoModel? _curVideo;
    private Dictionary<string, bool> _skipTimeStateDict = [];

    public PlayerListViewModel? PlayerListView { get; set; }

    public void Handle(OnEpisodeChangedMessage message)
    {
        if (message.Model == null) return;
        _curVideo = message.Model;

        //  设置收藏状态
        SetFavoriteState(message.Model);

        //  初始化跳转记录           
        _skipTimeStateDict.TryAdd(_curVideo.Key, false);

        Player?.OpenAsync(message.Model.VodPlayUrl);
    }


    private void InitializePlayer()
    {
        // Initializes Engine (Specifies FFmpeg libraries path which is required)
        Engine.Start(new EngineConfig()
        {
#if DEBUG
            LogOutput = ":debug",
            LogLevel = LogLevel.Debug,
            FFmpegLogLevel = FFmpegLogLevel.Warning,
#endif

            PluginsPath = $":{appSettingsConfig.PluginsPath}",
            FFmpegPath = $":{appSettingsConfig.FFmpegPath}",

            // Use UIRefresh to update Stats/BufferDuration (and CurTime more frequently than a second)
            UIRefresh = true,
            UIRefreshInterval = 100,
            UICurTimePerSecond =
                false // If set to true it updates when the actual timestamps second change rather than a fixed interval
        });

        Config = DefaultConfig();

        // Inform the lib to refresh stats
        if (Config != null)
        {
            Config.Player.Stats = true;

            Player = new Player(Config);
        }

        if (Player == null) return;
        if (_videoSettings != null) Player.Audio.Volume = _videoSettings.Volume;

        //  声音属性变化
        Player.Audio.PropertyChanged += Player_Audio_PropertyChanged;
        //  视频进度属性变化
        Player.PropertyChanged += Player_PropertyChanged;

        // Keep track of error messages
        Player.OpenCompleted += Player_OpenCompleted;
        Player.BufferingCompleted += Player_BufferingCompleted;
    }

    private void Player_BufferingCompleted(object? s, BufferingCompletedArgs e)
    {
        if (!string.IsNullOrEmpty(e.Error)) MessageQueue.Enqueue(e.Error);
    }

    private void Player_Audio_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        if (e.PropertyName != null && !e.PropertyName.Equals("Volume")) return;
        if (sender is Audio audio && _videoSettings != null)
            _videoSettings.Volume = audio.Volume;
    }

    private void Player_OpenCompleted(object? sender, OpenCompletedArgs e)
    {
        IsPlay = true;

        if (!string.IsNullOrEmpty(e.Error)) MessageQueue.Enqueue(e.Error);
        if (_curVideo == null) return;
        var skipTime = appService.GetPlaySkipTime(_curVideo.VodSourceID, _curVideo.VodId);
        SkipTimeSwitch = skipTime.SkipSwitch;
        SkipBeginPosition = skipTime.SkipBeginPosition;
        SkipEndPosition = skipTime.SkipEndPosition;

        SkipBegin();
    }

    private void Player_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        if (e.PropertyName != null && !e.PropertyName.Equals("CurTime")) return;
        if (sender is not Player player) return;
        //  跳转开关：开的情况
        //  时间条件：满足
        //  状态：未跳转
        if (_curVideo == null) return;
        if (!SkipTimeSwitch || !IsSkipEnd() || _skipTimeStateDict[_curVideo.Key]) return;
        //  重置跳转状态
        _skipTimeStateDict[_curVideo.Key] = true;
        //  下一集
        NavigateNext();
    }

    private static Config? DefaultConfig()
    {
        var config = new Config
        {
            Demuxer =
            {
                // Mainly for HLS to pass the original query which might includes session keys       
                FormatOptToUnderlying = true,
                //  加大缓存进度
                BufferDuration = 300 * (long)1000 * 10000
            },
            Audio =
            {
                // To allow embedded atempo filter for speed
                FiltersEnabled = true
            },
            Video =
            {
                // Set it empty so it will include it when we save it
                GPUAdapter = ""
            },
            Subtitles =
            {
                SearchLocal = true
            }
        };

        return config;
    }

    protected override void OnInitialActivate()
    {
        base.OnInitialActivate();

        _videoSettings = appService.GetVideoSettings();
        _playSkipTimes = [.. appService.GetPlaySkipTimes()];

        InitializePlayer();

        InitPlaylist();
    }

    private void SetFavoriteState(VideoModel? model)
    {
        var favorites = appService.GetFavorites();
        if (model != null)
            IsFavorite = favorites.FirstOrDefault(x => x.VodSourceID == model.VodSourceID
                                                       && x.VodId == model.VodId) != null;
    }

    private void InitPlaylist()
    {
        PlayerListView = container.Get<PlayerListViewModel>();

        PlayerListView.PropertyChanged -= PlayerListView_PropertyChanged;
        PlayerListView.PlayDict = PlayDict;
        PlayerListView.PlayMode = PlayMode;
        PlayerListView.PropertyChanged += PlayerListView_PropertyChanged;
        ActiveItem = PlayerListView;

        if (_videoSettings == null) return;
        PlayerListView.VideosSortByAsc = _videoSettings.VideosSortByAsc;
    }

    private void PlayerListView_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        if (e.PropertyName != null && !e.PropertyName.Equals("VideosSortByAsc")) return;
        if (sender is not PlayerListViewModel playerList) return;
        if (_videoSettings == null) return;
        _videoSettings.VideosSortByAsc = playerList.VideosSortByAsc;
    }

    protected override void OnActivate()
    {
        base.OnActivate();
        eventAggregator.Subscribe(this);
    }

    protected override void OnDeactivate()
    {
        base.OnDeactivate();
        eventAggregator.Unsubscribe(this);
    }

    protected override void OnClose()
    {
        base.OnClose();
        _videoSettings?.Save(appSettingsConfig.VideoConfig);
    }

    public void ExpendAll()
    {
        PlayerListWidth = PlayerListWidth == 300 ? 0 : 300;
    }

    public void WindowMinimize()
    {
        WindowState = WindowState.Minimized;
    }

    public void NavigateBefore()
    {
        PlayerListView?.NavigateBefore();
    }

    public void NavigateNext()
    {
        PlayerListView?.NavigateNext();
    }

    private void SkipTimeUpdate()
    {
        if (_curVideo == null) return;
        var skip = new PlaySkipTimeModel
        {
            SkipBeginPosition = SkipBeginPosition,
            SkipEndPosition = SkipEndPosition,
            SourceID = _curVideo.VodSourceID,
            VodId = _curVideo.VodId,
            SkipSwitch = SkipTimeSwitch
        };
        appService.PlaySkipTimeAddOrUpdate(skip);
    }

    private void SkipBegin()
    {
        var begin = TimeSpan.FromSeconds(SkipBeginPosition).Ticks;
        if (Player is { IsPlaying: true } && Player.CurTime < begin) Player.CurTime = begin;
    }

    private bool IsSkipEnd()
    {
        var end = TimeSpan.FromSeconds(SkipEndPosition).Ticks;
        return Player is { IsPlaying: true } && Player.Duration - Player.CurTime <= end;
    }

    private void FavoriteAddOrUpdate(VideoModel? model)
    {
        var favoriteModel = mapper.Map<FavoriteModel>(model);
        appService.FavoriteAddOrUpdate(favoriteModel);
    }

    private void FavoriteRemove(VideoModel? model)
    {
        var favoriteModel = mapper.Map<FavoriteModel>(model);
        appService.FavoriteTryRemove(favoriteModel.Key);
    }

    public void FavoriteChecked(object sender, RoutedEventArgs e)
    {
        IsFavorite = true;
        FavoriteAddOrUpdate(_curVideo);
    }

    public void FavoriteUnchecked(object sender, RoutedEventArgs e)
    {
        IsFavorite = false;
        FavoriteRemove(_curVideo);
    }

    public void WindowClose()
    {
        ToClose = true;
    }
}